package com.yiboshi.rehrs.helper;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.benmanes.caffeine.cache.Cache;
import com.yiboshi.rehrs.common.VarConstant;
import com.yiboshi.rehrs.config.RehrsSystemConfig;
import com.yiboshi.rehrs.config.ThreadLocalSession;
import com.yiboshi.rehrs.domain.entity.MyZhongUser;
import com.yiboshi.rehrs.domain.entity.SmZhongUser;
import com.yiboshi.rehrs.domain.entity.ZhongUser;
import com.yiboshi.rehrs.domain.model.TaskHostInfo;
import com.yiboshi.rehrs.domain.service.IZhongUserService;
import com.yiboshi.rehrs.exception.ZyantExceptionUtils;
import com.yiboshi.rehrs.param.AppConfig;
import com.yiboshi.rehrs.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author jack 2025/3/21
 */
@Component
@Slf4j
public class RedisHelper {

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Qualifier("HelperZhong_1")
    @Autowired
    HelperXiaofei helperXiaofei;

    @Autowired
    @Qualifier("HelperZhong_2")
    HelperDaxiang helperDaxiang;


    @Autowired
    @Qualifier("HelperZhong_3")
    HelperJinshe helperJinshe;

    @Autowired
    Map<String, HelperZhong> helperZhongMap;

    @Autowired
    IZhongUserService zhongUserService;


    String session_user_token_key = "z:session-a:token:";
    String session_user_account_key = "z:session-a:account:";

    String session_user_token_apk_key = "z:session-a:apk_token:";
    String session_user_account_apk_key = "z:session-a:apk_account:";

    public <T> T loadByCache(String key,Class<T> tClass){
        String s = stringRedisTemplate.opsForValue().get(key);
        if(StrUtils.isBlank(s)){
            return null;
        }
        if(tClass==String.class){
            return (T)s;
        }
        return JacksonUtils.toBean(s,tClass);
    }
    public void delCache(String key){
        stringRedisTemplate.delete(key);
    }
    public void save(String key,Object obj,long timeout, TimeUnit unit){
        if(obj==null){
            return ;
        }
        stringRedisTemplate.opsForValue().set(key, JacksonUtils.toString(obj),timeout,unit);
    }



    public void saveSessionUser(ZhongUser zhongUser,boolean isApk){
        String token = zhongUser.getToken();
        if(StrUtils.isBlank(token)){
            return;
        }
        String key = "app-token:"+token;
        stringRedisTemplate.opsForValue().set(key,zhongUser.getAccount(),7, TimeUnit.HOURS);

        String userKey = "app-account:"+zhongUser.getAccount();
        stringRedisTemplate.opsForValue().set(userKey, JSONObject.toJSONString(zhongUser),7, TimeUnit.HOURS);

        //String key = (isApk?session_user_token_apk_key:session_user_token_key) +token;
        //String keyAccount =  (isApk?session_user_account_apk_key:session_user_account_key)+ zhongUser.getAccount();
       //save(key, zhongUser,11, TimeUnit.HOURS);
       //save(keyAccount, token,11, TimeUnit.HOURS);
    }



    public boolean delSessionUser(String account){
        String key = "app-account:"+account;
        stringRedisTemplate.delete(key);
        return true;
    }

    public boolean delSessionUserToken(String token, String account, boolean isApk){
        String key = "app-token:"+token;
        stringRedisTemplate.delete(key);
        return true;
        /*String key = (isApk?session_user_token_apk_key:session_user_token_key)  +token;
        stringRedisTemplate.delete(key);
        stringRedisTemplate.delete((isApk?session_user_account_apk_key:session_user_account_key)+account);
        return  true;*/

    }
    public ZhongUser loadSessionUser(String token,boolean isApk){
        boolean mode = ThreadLocalSession.isMode(1);
        if(mode){
            return ThreadLocalSession.getSmUserInfo();
        }
        String key = "app-token:"+token;
        String account = stringRedisTemplate.opsForValue().get(key);
        if(StrUtils.isBlank(account)){
            return null;
        }
        if(System.currentTimeMillis()%5==0){
            stringRedisTemplate.expire(key,7,TimeUnit.HOURS);
        }
        String userKey =  "app-account:"+account;
        String userStr = stringRedisTemplate.opsForValue().get(userKey);
        if(StrUtils.isNotBlank(userStr)){
            if(System.currentTimeMillis()%5==0){
                stringRedisTemplate.expire(userStr,7,TimeUnit.HOURS);
            }
            return  JSONObject.parseObject(userStr, MyZhongUser.class);
        }
        ZhongUser zhongUser = zhongUserService.getOne(account, 0);
        if(zhongUser==null){
            stringRedisTemplate.delete(key);
            return null;
        }
        zhongUser.setToken(token);
        MyZhongUser myZhongUser = new MyZhongUser();
        BeanUtils.copyProperties(zhongUser,myZhongUser);
        stringRedisTemplate.opsForValue().set(userKey,JSONObject.toJSONString(myZhongUser),7,TimeUnit.HOURS);
        return  myZhongUser;
    }


    public String loadTaskToken(String account){
        String key = "n-logon-token:"+LocalDate.now();
        String token = (String) stringRedisTemplate.opsForHash().get(key, account);
        return token;
    }


    public AppConfig loadAppConfig(int currentVersion){
        String key = "system:config:check-update"+"-"+ThreadLocalSession.getMode()+":"
                + RehrsSystemConfig.getProfile()
                ;
        log.info("loadAppConfig: {}",key);
        AppConfig appConfig = CacheUtils.getByCache10Min(key, AppConfig.class);
        if(appConfig==null){
            String jsonStr = stringRedisTemplate.opsForValue().get(key);
            if(StrUtils.isBlank(jsonStr)){
                return null;
            }
            appConfig = JacksonUtils.toBean(jsonStr,AppConfig.class);
            CacheUtils.putByCache10Min(key, appConfig);
        }

        /*AppConfig appConfig = new AppConfig();
        appConfig.setMark("普通说明");
        appConfig.setH5DownUrl("https://wwlu.lanzouq.com/mayi100");
        appConfig.setStatus(AperStringUtils.randomNum(2));
        appConfig.setUpdateMark("优化了登录");
        appConfig.setMinVersion(100);
        appConfig.setLastVersion(200);*/
        if(currentVersion>=appConfig.getLastVersion()){
            if(ThreadLocalSession.isMode(0)){
                return null;
            }
            AppConfig appConfigNew = new AppConfig();
            appConfigNew.setFileDownUrl(appConfig.getFileDownUrl());
            //BeanUtils.copyProperties(appConfig,appConfigNew);
            //appConfig.setFileDownUrl("");
            //1
            return appConfigNew;
        }
        Integer minVersion = appConfig.getMinVersion();
        appConfig.setStatus(0);
        if(currentVersion<minVersion){
            appConfig.setStatus(1);
        }
        return appConfig;
    }

    public static void main(String[] args) {
        AppConfig appConfig = new AppConfig();
        appConfig.setMark("普通说明");
        appConfig.setH5DownUrl("https://wwlu.lanzouq.com/mayi100");
        appConfig.setStatus(AperStringUtils.randomNum(2));
        appConfig.setUpdateMark("优化了登录");
        appConfig.setMinVersion(100);
        appConfig.setLastVersion(200);
        System.out.println(JSON.toJSON(appConfig));
    }




    public HelperZhong selectHelper(String uri){
        return uri.contains("singleDeliver")?helperJinshe: this .selectHelper();
    }
    public void deleteHelper(String mode){
        Cache<String, Object> cache = CacheUtils.builder(60);
        cache.invalidate(VarConstant.enble_helper_mode);
        stringRedisTemplate.opsForSet().remove(VarConstant.enble_helper_mode,mode);
        cache.invalidate(VarConstant.enble_helper_mode);
    }
    public void addModeHelper(String mode){
        Cache<String, Object> cache = CacheUtils.builder(60);
        cache.invalidate(VarConstant.enble_helper_mode);
        stringRedisTemplate.opsForSet().add(VarConstant.enble_helper_mode,mode);
        cache.invalidate(VarConstant.enble_helper_mode);
    }

    public HelperZhong selectHelper(){
        /*String redisKey = helperDaxiang.vipTokenKey();
        if(!stringRedisTemplate.hasKey(redisKey)){
            return helperJinshe;
        }*/
        //String helper_key = "helper:error_*";
        //报错的
        Cache<String, Object> cache = CacheUtils.builder(10);
        Object o = cache.getIfPresent(VarConstant.enble_helper_mode);
        List<String> range = null;
        if(o!=null){
            range = (List<String>) o;
        }else{
            //stringRedisTemplate.opsForList().remove()
            Set<String> members = stringRedisTemplate.opsForSet().members(VarConstant.enble_helper_mode);
           // range = stringRedisTemplate.opsForSet().members(enble_helper_mode);
            range = new ArrayList<>(members);
           cache.put(VarConstant.enble_helper_mode,range);
        }
        if(CollUtil.isEmpty(range)){
            range = Arrays.asList( "2,3".split(","));
        }
        /*Set<String> keys = stringRedisTemplate.keys(helper_key);
        if(CollUtil.isNotEmpty(keys)){
            for (String key : keys) {
                key = key.substring(key.length()-1);
                range.remove(key);
            }
        }*/
        int i = AperStringUtils.randomNum(range.size());
        String s = range.get(i);
        HelperZhong helperZhong = helperZhongMap.get("HelperZhong_" + s);

        if(helperZhong==null){
            DingdingUtils.sendMessageByLocal(RehrsSystemConfig.getProfile()+"\n没有可用的授权通道了","18980482041");
            ZyantExceptionUtils.x(true,"授权异常，请稍后再试");
        }
        return helperZhong;

    }

    //type==1可用
    public void increase(int mode,int type){
        String helper_key = "helper:error:"+mode;
        String helper_key_2 = "helper:error_"+mode;
        if(type==1){
            //可用
            stringRedisTemplate.delete(helper_key_2);
            stringRedisTemplate.delete(helper_key);
            return;
        }
        Long increment = stringRedisTemplate.opsForValue().increment(helper_key, 1);
        stringRedisTemplate.expire(helper_key,4,TimeUnit.MINUTES);
        if(increment>=4){
            stringRedisTemplate.rename(helper_key,helper_key_2);
            stringRedisTemplate.expire(helper_key_2,30,TimeUnit.MINUTES);
        }
    }


    public void addIp(String ip){
        stringRedisTemplate.opsForSet().add("system:config:ip_list",ip);
    }


    public void delByPattern(String pattern){
        Set<String> keys = stringRedisTemplate.keys(pattern);
        if(CollUtil.isNotEmpty(keys)){
            stringRedisTemplate.delete(keys);
        }
    }


    static Cache<String, Object> taskHostInfoCache = CacheUtils.builder(30);

    public TaskHostInfo loadTaskHostInfo(boolean cache){
        String key = RehrsSystemConfig.getProfile() + "-" + RehrsSystemConfig.getServerIpAndPort();
        if(cache){
            Object o = taskHostInfoCache.getIfPresent(key);
            if(o!=null){
                TaskHostInfo taskHostInfo = (TaskHostInfo) o;
                taskHostInfo.setKey(key);
                return taskHostInfo;
            }
        }
        Object o = stringRedisTemplate.opsForHash().get(VarConstant.TASK_HOST_INFO_KEY, key);
        TaskHostInfo taskHostInfo = null;
        if (o==null){
            taskHostInfo = new TaskHostInfo();
            taskHostInfo.setKey(key);
            return taskHostInfo;
        }
        taskHostInfo = JacksonUtils.toBean(o.toString(), TaskHostInfo.class);
        taskHostInfo.setKey(key);

        return taskHostInfo;
    }
    public void saveTaskHostInfo(TaskHostInfo taskHostInfo){
        String key = taskHostInfo.getKey();
        taskHostInfoCache.put(key,taskHostInfo);
        taskHostInfo.setKey("");
        stringRedisTemplate.opsForHash().put(VarConstant.TASK_HOST_INFO_KEY,key, JacksonUtils.toString(taskHostInfo));
    }

    public void smSaveSession(ZhongUser userInfo) {
        String key = "smurfs:session:"+userInfo.getToken();
        CacheUtils.putByCache10Min(key, userInfo);
        String account = userInfo.getAccount();
        stringRedisTemplate.opsForValue().set(key,account,9,TimeUnit.HOURS);
        /*
        String user_key = sm_user_key+account;
        stringRedisTemplate.opsForValue().set(user_key,JacksonUtils.toString(userInfo),48,TimeUnit.HOURS);
        CacheUtils.putByCache10Min(user_key,userInfo);
         */

       /* if(StrUtils.isBlank(substitutionOrgListString)||substitutionOrgListString.length()>10){
            String substitution_key = "sm:substitution:"+account;
            stringRedisTemplate.opsForValue().set(substitution_key,substitution_key,12,TimeUnit.HOURS);
        }*/

        //String key2 =key + "-substitutionOrgList";
        //stringRedisTemplate.opsForValue().set(key2,substitutionOrgListString,9,TimeUnit.HOURS);

    }
    String sm_user_key = "smurfs:user:";
    public void delSmLoadSession(String token,String account) {
        if(StrUtils.isNotBlank(token)){
            String key = "smurfs:session:"+token;
            CacheUtils.delByCache10Min(key,String.class);
        }
        String user_key = sm_user_key+account;
        CacheUtils.delByCache10Min(user_key,SmZhongUser.class);
        stringRedisTemplate.delete(user_key);
    }
    public SmZhongUser smLoadSessionByAccount(String account) {
        String user_key = sm_user_key+account;
        SmZhongUser byCache10Hour = CacheUtils.getByCache10Min(user_key, SmZhongUser.class);
        if(byCache10Hour!=null){
            return byCache10Hour;
        }
        String o = stringRedisTemplate.opsForValue().get(user_key);
        if(o!=null){
            byCache10Hour  = JacksonUtils.toBean(o, SmZhongUser.class);
        }else{
            byCache10Hour = zhongUserService.getSmUser(account);

            if(byCache10Hour!=null){
                CacheUtils.putByCache10Min(user_key,byCache10Hour);
                stringRedisTemplate.opsForValue().set(user_key,JacksonUtils.toString(byCache10Hour)
                        ,12,TimeUnit.HOURS);
            }
        }
        return byCache10Hour;
    }

    public SmZhongUser smLoadSession(String token) {
        String key = "smurfs:session:"+token;
        String account = CacheUtils.getByCache10Min(key, String.class);
        if(StrUtils.isBlank(account)){
            account = stringRedisTemplate.opsForValue().get(key);
            if(StrUtils.isNotBlank(account)){
                CacheUtils.putByCache10Min(key,account);
            }
        }
        if(StrUtils.isBlank(account)){
            return null;
        }
        SmZhongUser smZhongUser = smLoadSessionByAccount(account);
        if(smZhongUser==null){
            return null;
        }
        if(StrUtils.isNotBlank(token)){
            smZhongUser.setToken(token);
        }
        return smZhongUser;
    }

    public String defaultImei() {
        //return "867382035888381";
        return "351564710852443";
    }

    public void inviteCode(){

    }

    public String getInviteCode(){
        while(true){
            String inviteCode = RechargeCardUtil.inviteCode();
            boolean present = stringRedisTemplate.opsForHash().putIfAbsent("system:config:invite_code", inviteCode, LocalDateTime.now());
            if(present){
                return inviteCode;
            }
        }
    }


    public void addYuyueToken(String account,String token){
        String logonByFace_key = CommonUtil.getLogonTokenKey(LocalDate.now());
        stringRedisTemplate.opsForHash().put(logonByFace_key,account,token);
    }


    /**
     * 原子性地从ZSet中获取并删除大于指定分数的所有成员
     * @param key ZSet的键名
     * @param minScore 最小分数（不包含）
     * @return 被删除的成员列表
     */
    public Set<String> popMembersByScore(String key, double minScore) {
        String luaScript =
                "local members = redis.call('ZREVRANGEBYSCORE', KEYS[1], '+inf', ARGV[1], 'WITHSCORES') " +
                        "if #members > 0 then " +
                        "    for i = 1, #members, 2 do " +
                        "        redis.call('ZREM', KEYS[1], members[i]) " +
                        "    end " +
                        "end " +
                        "return members";

        List<String> result = (List<String>) stringRedisTemplate.execute(
                (RedisCallback<List<String>>) connection ->
                        connection.eval(
                                luaScript.getBytes(),
                                ReturnType.MULTI,
                                1,
                                key.getBytes(),
                                String.valueOf(minScore).getBytes()
                        ),
                true
        );

        Set<String> members = new LinkedHashSet<>();
        if (result != null) {
            for (int i = 0; i < result.size(); i += 2) {
                if (i < result.size()) {
                    members.add(result.get(i));
                }
            }
        }
        return members;
    }

    /**
     * 原子性地从ZSet中获取并删除大于指定分数的一个成员
     * @param key ZSet的键名
     * @param minScore 最小分数（不包含）
     * @return 被删除的成员，如果没有符合条件的成员则返回null
     */
    public String popOneMemberByScore(String key, double minScore) {
        String luaScript =
                "local members = redis.call('ZREVRANGEBYSCORE', KEYS[1], '+inf', ARGV[1], 'LIMIT', 0, 1) " +
                        "if #members > 0 then " +
                        "    redis.call('ZREM', KEYS[1], members[1]) " +
                        "    return members[1] " +
                        "else " +
                        "    return nil " +
                        "end";

        return stringRedisTemplate.execute(
                (RedisCallback<String>) connection ->
                        connection.eval(
                                luaScript.getBytes(),
                                ReturnType.VALUE,
                                1,
                                key.getBytes(),
                                String.valueOf(minScore).getBytes()
                        ),
                true
        );
    }

    /**
     * 原子性地从ZSet中获取并删除分数在指定范围内的成员
     * @param key ZSet的键名
     * @param minScore 最小分数（包含）
     * @param maxScore 最大分数（包含）
     * @return 被删除的成员列表
     */
    public Set<String> popMembersByScoreRange(String key, double minScore, double maxScore) {
        String luaScript =
                "local members = redis.call('ZRANGEBYSCORE', KEYS[1], ARGV[1], ARGV[2], 'WITHSCORES') " +
                        "if #members > 0 then " +
                        "    for i = 1, #members, 2 do " +
                        "        redis.call('ZREM', KEYS[1], members[i]) " +
                        "    end " +
                        "end " +
                        "return members";

        List<String> result = (List<String>) stringRedisTemplate.execute(
                (RedisCallback<List<String>>) connection ->
                        connection.eval(
                                luaScript.getBytes(),
                                ReturnType.MULTI,
                                1,
                                key.getBytes(),
                                String.valueOf(minScore).getBytes(),
                                String.valueOf(maxScore).getBytes()
                        ),
                true
        );

        Set<String> members = new LinkedHashSet<>();
        if (result != null) {
            for (int i = 0; i < result.size(); i += 2) {
                if (i < result.size()) {
                    members.add(result.get(i));
                }
            }
        }
        return members;
    }


    //public void smSaveToken(String token,String  account, String ip){}
}
